#I Import and Initialize
import  sys, os
import pygame
import random
from data import *
from sprites import *
from scenes import *
from level import *
import menu



#############################################################################
# Returns the actor's rect relative to the camera.
#############################################################################
def RelRect(actor, camera):
    return Rect(actor.rect.x-camera.rect.x, actor.rect.y-camera.rect.y, actor.rect.w, actor.rect.h)



#############################################################################
# Counts the number of levels available.
#############################################################################
def count_levels():
        i = 1
        while os.path.exists(filepath("level%d.png" % i)) == True:
            i += 1
        return i - 1



#############################################################################
# Represents the camera in which the players moves.
#############################################################################
class Camera(object):
    


    #############################################################################
    # Initializes a new instance of Camera.
    #############################################################################
    def __init__(self, player, width, level):
        self.player         = player
        self.rect           = pygame.display.get_surface().get_rect()
        self.world          = Rect(0, 0, width, 480)
        self.rect.center    = self.player.rect.center
        self.level          = level
    


    #############################################################################
    # Updates this camera's boundaries.
    #############################################################################    
    def update(self):
        if self.rect.right <= (self.level.get_rect().right * 32):
            if self.player.rect.centerx > self.rect.centerx+64:
                self.rect.centerx = self.player.rect.centerx-64
            if self.player.rect.left < self.rect.left:
                self.player.rect.left = self.rect.left
            self.rect.clamp_ip(self.world)
    


    #############################################################################
    # Draws all sprites that are within this camera's boundaries.
    #############################################################################    
    def draw_sprites(self, surf, sprites):
        for s in sprites:
            if s.rect.colliderect(self.rect):
                surf.blit(s.image, RelRect(s, self))



#############################################################################
# Represents this game itself.
#############################################################################                
class Game(object):
    


    #############################################################################
    # Initializes a new instance of Game.
    #############################################################################
    def __init__(self, screen, lvl=1):
        
        #E Entities
        self.screen         = screen
        self.birdUps        = loadImage("bird-ups.png")
        self.background     = loadImage("background.png")
        self.explosionSound = load_sound("explosion.ogg")
        self.liveupSound    = load_sound("liveup.ogg")
        
        #A Assign
        self.sprites                = pygame.sprite.OrderedUpdates()
        self.playerSprites          = pygame.sprite.OrderedUpdates()
        self.ballSprites            = pygame.sprite.OrderedUpdates()
        self.enemySprites           = pygame.sprite.OrderedUpdates()
        self.explosionSprites       = pygame.sprite.OrderedUpdates()
        self.bulletSprites          = pygame.sprite.OrderedUpdates()
        self.solidSprites           = pygame.sprite.OrderedUpdates()
        self.nonSolidSprites        = pygame.sprite.OrderedUpdates()
        self.liveupSprites          = pygame.sprite.OrderedUpdates()
        self.finishlineSprites      = pygame.sprite.OrderedUpdates()
        
        Player.groups         = self.sprites, self.playerSprites
        PlayerDef.groups      = self.sprites, self.ballSprites
        Enemy.groups          = self.sprites, self.enemySprites
        Explosion.groups      = self.sprites, self.explosionSprites
        EnemyBullet.groups    = self.sprites, self.bulletSprites
        SolidObject.groups    = self.sprites, self.solidSprites
        NonSolidObject.groups = self.sprites, self.nonSolidSprites
        LiveUp.groups         = self.sprites, self.liveupSprites
        FinishLine.groups     = self.sprites, self.finishlineSprites
        
        self.events      = None        
        self.score       = 0
        self.lives       = 3
        self.time        = 400
        self.lvl         = lvl
        self.font        = pygame.font.Font(filepath("fonts/font1.ttf"), 16)
        self.speed       = [4, 4]
        self.bgspeed     = [-1,0]
        self.white       = 255, 255, 255
        self.clock       = pygame.time.Clock()
        self.keepGoing   = True
        self.frame       = 0
        
    #A Action
        self.next_level(0)
        self.main_loop()
    


    #############################################################################
    # Gets the current level.
    #############################################################################    
    def get_lvl(self):
        return self.lvl
    


    #############################################################################
    # Gets the saved score of a given level.
    #############################################################################    
    def get_lvl_score(self, lvl):
        try:
            return int(open(filepath("..\saves\lvl%d.sav" % lvl)).read())
        except:
            return 0
    


    #############################################################################
    # Saves the current level and its score to disk.
    #############################################################################
    def save_lvl(self, lvl, score):
        if self.get_lvl_score(lvl) <= score:
            open(filepath("..\saves\lvl%d.sav" % lvl), "w").write(str(score))
    


    #############################################################################
    # Displays the 'Game Over' screen.
    #############################################################################    
    def gameover_screen(self):
        scene(self.screen, ["Game Over"])
        self.keepGoing = False
    


    #############################################################################
    # Displays the finish screen.
    #############################################################################    
    def finish_screen(self):
        self.save_lvl(self.lvl, self.score)
        if self.lvl < count_levels():
            scene(self.screen, ["Level Complete"], self)
            self.next_level()
        else:
            scene(self.screen, ["Game Complete"], self)
            self.keepGoing = False
    


    #############################################################################
    # Draws the player's lives, score and framerate in the topright corner.
    #############################################################################
    def draw_stats(self):
        firstLine = 16
        secondLine = 35
        
        lives = self.lives
        if lives < 0:
            lives = 0
        
        ren = self.font.render("Score: %05d" % self.score, 1, (255, 255, 255))
        self.screen.blit(ren, (608, firstLine))
               
        ren = self.font.render("Level: %0d" % self.lvl, 1, (255, 255, 255))
        self.screen.blit(ren, (608, secondLine))
        
        ren = self.font.render("x%d" % lives, 1, (255, 255, 255))
        self.screen.blit(ren, (478, firstLine))
               
        ren = self.font.render("FPS: %d" % self.clock.get_fps(), 1, (255, 255, 255))
        self.screen.blit(ren, (478, secondLine))
        
        self.screen.blit(self.birdUps, (420, firstLine))
    


    #############################################################################
    # Executes when the spacebar is pressed.
    #############################################################################
    def space_pressed(self):
        PlayerDef((self.player.rect.centerx - 30), self.player.rect.centery)
    


    #############################################################################
    # Advances to the next level if available.
    #############################################################################        
    def next_level(self, next = 1):
        self.lvl += next
        self.kill_sprites()
        self.player      = Player(10, 60, self.events)
        self.level       = Level(self.player, self.lvl)
        self.camera      = Camera(self.player, (self.screen.get_rect().width * 32), self.level)
    


    #############################################################################
    # Kills all sprites.
    #############################################################################    
    def kill_sprites(self):
        for s in self.sprites:
            pygame.sprite.Sprite.kill(s)
           
           
                    
    #L Loop
    #############################################################################
    # Continuously updates all sprites and the camers itself.
    #############################################################################
    def main_loop(self):    
        while self.keepGoing:
            #T Timer (framerate)
            self.events = pygame.event.get()
            self.player.events = self.events
            self.frame += 1
            self.clock.tick(90)

            #E Event
            self.player.collide(self.solidSprites)
            
            for f in self.finishlineSprites:
                if self.player.rect.colliderect(f.rect):
                    self.finish_screen()

            for l in self.liveupSprites:
                if self.player.rect.colliderect(l.rect):
                    self.liveupSound.play()
                    l.kill()
                    self.lives += 1
                                               
            for b in self.bulletSprites:
                if self.player.rect.colliderect(b.rect):
                    b.kill()
                    if b.get_type() == "cannon":
                        self.score -= 50
                        Explosion(b.rect.center, "vifty")
                    else:
                        self.score -= 60
                        Explosion(b.rect.center, "sixty")
                if b.rect.left < self.camera.rect.left:
                    b.kill()
                if b.rect.right > self.camera.rect.right:
                    b.kill()
                if b.rect.top < self.camera.rect.top:
                    b.kill()
                if pygame.sprite.spritecollideany(b, self.solidSprites):
                    b.kill()
                
            for e in self.enemySprites:
                if self.camera.rect.colliderect(e.rect):
                    e.canshoot = True
                else:
                    e.canshoot = False
                if self.player.rect.colliderect(e.rect):
                    self.explosionSound.play()
                    Explosion(e.rect.center, "up")
                    e.kill()
                    if self.lives <= 1:
                        self.score = -1;
                    else:
                        self.lives -=1
                if pygame.sprite.spritecollideany(e, self.ballSprites):
                    self.explosionSound.play()
                    if e.get_type() == "cannon": 
                        e.kill()
                        Explosion(e.rect.center, "cannon")
                        self.score += 15
                    if e.get_type() == "gunner" and e.get_hits() < 2:
                        e.hits_increment()
                        e.blink()
                    if e.get_type() == "gunner" and e.get_hits() == 2:
                        e.kill()
                        Explosion(e.rect.center, "gunner")
                        self.score += 25
                if e.rect.right < self.camera.rect.left:
                    e.kill()
                    
            for b in self.ballSprites:
                if pygame.sprite.spritecollideany(b, self.solidSprites):
                    b.kill()
                if pygame.sprite.spritecollideany(b, self.enemySprites):
                    b.kill()
            
            if self.score < 0 and self.lives <= 1:
                self.gameover_screen()
            elif self.score < 0:
                self.lives -= 1
                self.score = 0
                
            #R Refresh
            self.camera.update()
            self.playerSprites.update()
            self.ballSprites.update()
            self.enemySprites.update()
            self.bulletSprites.update()
            self.explosionSprites.update()
            
            self.screen.fill(self.white)
            self.screen.blit(self.background, self.background.get_rect())
            
            for event in self.events:
                if event.type == pygame.QUIT:
                    sys.exit()
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        self.keepGoing = False
            
            self.camera.draw_sprites(self.screen, self.sprites)

            self.draw_stats()
            
            pygame.display.flip()
